package com.tianji.learning.util;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.tianji.common.utils.JsonUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.pojo.po.LearningLesson;
import com.tianji.learning.pojo.po.LearningRecord;
import com.tianji.learning.service.ILearningLessonService;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.*;

@Slf4j
@Component
@RequiredArgsConstructor
public class LearningRecordDelayTaskHandler {

    private final StringRedisTemplate redisTemplate;
    private final LearningRecordMapper recordMapper;
    private final ILearningLessonService lessonService;
    private final DelayQueue<DelayTask<RecordTaskData>> queue = new DelayQueue<>();
    private final static String RECORD_KEY_TEMPLATE = "learning:record:{}";
    private static volatile boolean begin = true;

    @PostConstruct
    public void init() {
        log.info("Bean初始化阶段，开启一个新的线程执行延迟检测任务");
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 1, TimeUnit.HOURS,
                new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()
        );
        CompletableFuture.runAsync(this::handleDelayTask, executor);
    }

    @PreDestroy
    public void preDestroy() {
        log.info("容器销毁，延迟检测任务停止执行");
        begin = false;
    }

    public void handleDelayTask() {

        while (begin) {
            try {
                // 获取到期的延迟任务，【延迟任务到期后才会被获取到，不会出现阻塞】
                DelayTask<RecordTaskData> delayTask = queue.take();

                RecordTaskData taskData = delayTask.getData();
                // 查询redis缓存返回 record对象
                LearningRecord record = readRedisData(taskData.getLessonId(), taskData.getSectionId());
                if (ObjectUtil.isNull(record)) {
                    continue;
                }

                // 比较提交过来的moment值与缓存中保存的moment值是否一致
                if (!ObjectUtil.equals(taskData.getMoment(), record.getMoment())) {
                    continue;
                }

                // 一致表示用户长时间未看视频
                // 更新学习记录信息，更新课表数据
                record.setFinished(null);
                recordMapper.updateById(record);

                LearningLesson lesson = new LearningLesson();
                lesson.setId(taskData.lessonId);
                lesson.setLatestSectionId(taskData.sectionId);
                lesson.setLatestLearnTime(LocalDateTime.now());
                lessonService.updateById(lesson);
            } catch (InterruptedException e) {
                log.error("处理延迟任务发生异常", e);
            }
        }

    }

    /**
     * 读取redis缓存
     *
     * @param lessonId
     * @param sectionId
     * @return
     */
    public LearningRecord readRedisData(Long lessonId, Long sectionId) {
        // 拼接redis的大key
        try {
            String key = StringUtils.format(RECORD_KEY_TEMPLATE, lessonId);
            Object redisData = redisTemplate.opsForHash().get(key, sectionId.toString());

            if (ObjectUtil.isNull(redisData)) {
                return null;// 表示缓存中未查找到数据
            }
            return JSONUtil.toBean(redisData.toString(), LearningRecord.class);
        } catch (Exception e) {
            log.error("缓存读取异常", e);
            e.printStackTrace();
            return null;
        }

    }

    public void addLearningRecord(LearningRecord record) {
        // 添加数据到redis缓存
        writeRecordCache(record);

        // 提交延迟任务到延迟队列
        queue.add(new DelayTask<>(new RecordTaskData(record), Duration.ofSeconds(20)));
    }

    public void writeRecordCache(LearningRecord record) {
        try {
            log.debug("更新学习记录的缓存数据");
            // 将数据转换为延迟任务类型JSON字符串
            String jsonStr = JsonUtils.toJsonStr(new RecordTaskData(record));

            String key = StringUtils.format(RECORD_KEY_TEMPLATE, record.getLessonId());
            redisTemplate.opsForHash().put(key, record.getSectionId().toString(), jsonStr);
            redisTemplate.expire(key, Duration.ofMillis(1));
        } catch (Exception e) {
            log.error("更新学习记录缓存异常", e);
        }
    }

    public void cleanRecordCache(Long lessonId, Long sectionId) {
        String key = StringUtils.format(RECORD_KEY_TEMPLATE, lessonId);
        redisTemplate.opsForHash().delete(key, sectionId.toString());
    }

    @Data
    @NoArgsConstructor
    private static class RecordTaskData {
        private Long lessonId;
        private Long sectionId;
        private Integer moment;

        public RecordTaskData(LearningRecord record) {
            this.lessonId = record.getLessonId();
            this.sectionId = record.getSectionId();
            this.moment = record.getMoment();
        }
    }
}